#include "threading_Thread.h"
#include "pika_platform_thread.h"
#include "PikaVM.h"
#include "TinyObj.h"
#include "dataMemory.h"

#define LOG pika_platform_printf

#if !PIKASCRIPT_VERSION_REQUIRE_MINIMUN(1, 13, 4)
#error "pikapython version must be greater than 1.13.4"
#endif

static volatile int g_thread_stack_size = PIKA_THREAD_STACK_SIZE;
extern volatile PikaMemInfo g_PikaMemInfo;

//好像不必有
// #if PIKA_FREERTOS_ENABLE
// static pika_platform_thread_t* g_thread_to_free = NULL;
// static pika_platform_thread_lock_t g_thread_idle_hook_mutex = {0};
// static int g_thread_idle_hook_mutex_inited = 0;
// #endif


typedef struct pika_thread_info_t {
    Arg* function;
    Arg* args;
    pika_platform_thread_t* thread;
    int stack_size;
    // TODO 加上一个互斥锁
    // pika_platform_thread_lock_t* lock;
    char* name;
    pika_platform_thread_semaphore_t* _thread_end_sem; //线程结束信号
    pika_platform_thread_semaphore_t* _obj_deleted_sem;//对象删除信号
} pika_thread_info_t;


static void _thread_func(void* arg);

void threading_Thread___init__(PikaObj *self, char* name, Arg* target, Arg* args_){
    pika_thread_info_t* thread_info = pikaMalloc(sizeof(pika_thread_info_t));
    pika_platform_memset(thread_info, 0, sizeof(pika_thread_info_t));
    thread_info->function = arg_copy(target);
    if (arg_isObject(args_)) {
        PikaObj* tuple = arg_getPtr(args_);
        size_t tuple_size = pikaTuple_getSize(tuple);
        pika_debug("type of args is %d", arg_getType(args_));
        pika_debug("new_thread: args tuple size %d", tuple_size);
        if (tuple_size > 0) {
            thread_info->args = arg_copy(args_);
        }
    } else {
        thread_info->args = arg_copy(args_);
    }

    thread_info->name = pikaMalloc(strlen(name) + 1);
    strCopy(thread_info->name, name);
    pika_platform_thread_lock_t* _started_lock = pikaMalloc(sizeof(pika_platform_thread_lock_t));
    pika_platform_thread_lock_init(_started_lock);
    obj_setPtr(self, "thread_info", thread_info);
    obj_setPtr(self, "_started_lock", _started_lock);

    pika_platform_thread_semaphore_t* _thread_end_sem = pikaMalloc(sizeof(pika_platform_thread_semaphore_t));
    pika_platform_thread_semaphore_binary_init(_thread_end_sem);
    pika_platform_thread_semaphore_acquire(_thread_end_sem, pika_true, -1); //立即使线程结束信号无效，在线程结束时再使之有效
    thread_info->_thread_end_sem = _thread_end_sem;


    pika_platform_thread_semaphore_t* _obj_deleted_sem = pikaMalloc(sizeof(pika_platform_thread_semaphore_t));
    pika_platform_thread_semaphore_binary_init(_obj_deleted_sem);
    pika_platform_thread_semaphore_acquire(_obj_deleted_sem, pika_true, -1); //立即使对象删除信号无效，在__del__中再使之有效
    thread_info->_obj_deleted_sem = _obj_deleted_sem;

}

pika_bool threading_Thread_join(PikaObj *self, Arg* timeout){
    ArgType timout_type = arg_getType(timeout);
    pika_float timeout_f;
    if (timout_type == ARG_TYPE_FLOAT){
        timeout_f = arg_getFloat(timeout);
        if (timeout_f < 0.0){
            obj_raise(self, PIKA_RES_ERR_VALUE_ERROR, "timeout shouldn't less than 0.");
            return pika_false;
        }
    }
    else if (timout_type == ARG_TYPE_INT){
        timeout_f = (pika_float)arg_getInt(timeout);
        if (timeout_f < 0.0){
            obj_raise(self, PIKA_RES_ERR_VALUE_ERROR, "timeout shouldn't less than 0.");
            return pika_false;
        }
    }
    else if (timout_type == ARG_TYPE_NONE){
        timeout_f = -1;
    }
    else{
        obj_raise(self, PIKA_RES_ERR_TYPE_ERROR, "float, int or None is required.");
        return pika_false;
    }
    pika_thread_info_t* thread_info = obj_getPtr(self, "thread_info");
    pika_platform_thread_lock_t* _started_lock = obj_getPtr(self, "_started_lock");
    if (pika_platform_thread_lock_acquire(_started_lock, pika_false, 0) == PIKA_RES_OK){
        pika_platform_thread_lock_release(_started_lock);
        obj_raise(self, PIKA_RES_ERR_RUNTIME_ERROR, "cannot join thread before it is started.");
        return pika_false;
    }
    LOG("######## join acquire ##########");
    //信号无效表示线程未结束
    if (pika_platform_thread_semaphore_acquire(thread_info->_thread_end_sem, pika_true, timeout_f) != PIKA_RES_OK){
        return pika_false;
    }
    //信号有效表上线程已结束，join可以返回
    LOG("######## join release ##########");
    pika_platform_thread_semaphore_release(thread_info->_thread_end_sem);
    return pika_true;
}

void threading_Thread_start(PikaObj *self){
    pika_platform_thread_lock_t* _started_lock = obj_getPtr(self, "_started_lock");
    pika_platform_thread_lock_acquire(_started_lock, pika_true, -1);
    pika_thread_info_t* thread_info = obj_getPtr(self, "thread_info");

    _VM_lock_init();

    if (!_VM_is_first_lock()) {
        pika_debug("first lock for main thread");
        pika_GIL_ENTER();
        pika_debug("VM num %d", _VMEvent_getVMCnt());
    }

    thread_info->stack_size = g_thread_stack_size;
    pika_debug("thread stack size %d", thread_info->stack_size);
    thread_info->thread = pika_platform_thread_init("pika_thread", _thread_func, thread_info,
                                             thread_info->stack_size, PIKA_THREAD_PRIO,
                                             PIKA_THREAD_TICK);
    if (NULL == thread_info->thread) {
        pikaFree(thread_info, sizeof(pika_thread_info_t));
        obj_raise(self, PIKA_RES_ERR_RUNTIME_ERROR, "thread create failed");
        return;
    }
    pika_platform_thread_startup(thread_info->thread);
    g_PikaMemInfo.heapUsed += thread_info->stack_size;

}
void threading_Thread___del__(PikaObj *self){
    pika_thread_info_t* thread_info = obj_getPtr(self, "thread_info");
    pika_platform_thread_lock_t* _started_lock = obj_getPtr(self, "_started_lock");
    int thread_is_started = 0;
    if (_started_lock != NULL){
        //_started_lock锁能获取表示线程从未启动过
        thread_is_started = pika_platform_thread_lock_acquire(_started_lock, pika_false, 0) == PIKA_RES_OK ? 0 : 1;
    }
    if (thread_is_started && pika_platform_thread_semaphore_acquire(thread_info->_thread_end_sem, pika_false, 0) != PIKA_RES_OK){
        //线程已启动过且线程结束信号无效（线程未结束），表示Thread对象销毁后线程仍继续运行，保留线程结束信号和对象删除信号，使对象删除信号有效
            LOG("######## thread has started and not end ##########");
            pika_platform_thread_semaphore_release(thread_info->_obj_deleted_sem);
    }   
    else{ //线程未启动过或者线程启动过且线程结束信号有效（线程已结束），表示资源可以全部释放
        LOG("######## thread not start yet or has end ##########");
        if (thread_info->_thread_end_sem != NULL){
            pika_platform_thread_semaphore_destroy(thread_info->_thread_end_sem);
            pikaFree(thread_info->_thread_end_sem, sizeof(pika_platform_thread_semaphore_t));
        }
        if (thread_info->_obj_deleted_sem != NULL){
            pika_platform_thread_semaphore_destroy(thread_info->_obj_deleted_sem);
            pikaFree(thread_info->_obj_deleted_sem, sizeof(pika_platform_thread_semaphore_t));
        }
        if (thread_info->name != NULL){
            pikaFree(thread_info->name, strlen(thread_info->name) + 1);
        }
        if (thread_info != NULL){
            pikaFree(thread_info, sizeof(pika_thread_info_t));
        }
    }
    
    if (_started_lock != NULL){
        pika_platform_thread_lock_destroy(_started_lock);
        pikaFree(_started_lock, sizeof(pika_platform_thread_lock_t));
    }


}


static void _thread_func(void* arg) {
    pika_debug("thread start");
    pika_GIL_ENTER();
    LOG("######## thread start ##########");
    pika_thread_info_t* thread_info = (pika_thread_info_t*)arg;
    pika_platform_thread_t* _thread = thread_info->thread;
    pika_platform_thread_semaphore_t* _thread_end_sem = thread_info->_thread_end_sem;
    pika_platform_thread_semaphore_t* _obj_deleted_sem = thread_info->_obj_deleted_sem;

    PikaVMThread* vmThread = pikaVMThread_require();
    vmThread->is_sub_thread = 1;
    PikaObj* ctx = New_TinyObj(NULL);
    
    if (NULL != thread_info->args) {
        obj_setArg(ctx, "args", thread_info->args);
    }
    obj_setArg(ctx, "thread", thread_info->function);
    
    if (NULL == thread_info->args) {
        /* clang-format off */
        PIKA_PYTHON(
        thread()
        )
        /* clang-format on */
        const uint8_t bytes[] = {
            0x04, 0x00, 0x00, 0x00, /* instruct array size */
            0x00, 0x82, 0x01, 0x00, /* instruct array */
            0x08, 0x00, 0x00, 0x00, /* const pool size */
            0x00, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x00, /* const pool */
        };
        pikaVM_runByteCode(ctx, (uint8_t*)bytes);
    } else {
        /* clang-format off */
        PIKA_PYTHON(
            thread(*args)
        )
        /* clang-format on */
        const uint8_t bytes[] = {
            0x0c, 0x00, 0x00, 0x00, /* instruct array size */
            0x20, 0x81, 0x01, 0x00, 0x10, 0x08, 0x06, 0x00, 0x00, 0x02, 0x08,
            0x00,
            /* instruct array */
            0x0f, 0x00, 0x00, 0x00, /* const pool size */
            0x00, 0x61, 0x72, 0x67, 0x73, 0x00, 0x2a, 0x00, 0x74, 0x68, 0x72,
            0x65, 0x61, 0x64, 0x00, /* const pool */
        };
        pikaVM_runByteCode(ctx, (uint8_t*)bytes);
    }
    
    obj_deinit(ctx);
    arg_deinit(thread_info->function);
    if (NULL != thread_info->args) {
        arg_deinit(thread_info->args);
    }
    g_PikaMemInfo.heapUsed -= thread_info->stack_size;
    pika_debug("thread exiting");
    // TODO 加入释放锁的逻辑
    LOG("######## thread exit ##########");
    // pika_platform_thread_lock_release(_lock);
    // LOG("######## mutex_unlock ##########");

    //先判断Thread对象是否被删除
    int result = pika_platform_thread_semaphore_acquire(_obj_deleted_sem, pika_false, 0);
    if (result != PIKA_RES_OK){ //没被删除，需要通知Thread对象本线程已结束
        LOG("######## obj is alive ##########");
        pika_platform_thread_semaphore_release(_thread_end_sem);
        
    }
    else{ //Thread对象已被删除，线程状态已不可被外界获取，直接删除两个信号即可
        LOG("######## obj is deleted ##########");
        pika_platform_thread_semaphore_destroy(_thread_end_sem);
        pika_platform_thread_semaphore_destroy(_obj_deleted_sem);
        pikaFree(_thread_end_sem, sizeof(pika_platform_thread_semaphore_t));
        pikaFree(_obj_deleted_sem, sizeof(pika_platform_thread_semaphore_t));

        pikaFree(thread_info->name, strlen(thread_info->name) + 1);
        pikaFree(thread_info, sizeof(pika_thread_info_t));
    }
    
    pika_GIL_EXIT();
#if PIKA_FREERTOS_ENABLE
    pikaFree(_thread, sizeof(pika_platform_thread_t));
    pika_platform_thread_exit(NULL);
#else
    pika_platform_thread_exit(_thread);
#endif
    
}

// int _thread_stack_size(PikaObj* self, PikaTuple* size) {
//     if (pikaTuple_getSize(size) == 1) {
//         int stack_size = pikaTuple_getInt(size, 0);
//         if (stack_size == 0) {
//             stack_size = PIKA_THREAD_STACK_SIZE;
//         }
//         g_thread_stack_size = stack_size;
//     }
//     return g_thread_stack_size;
// }
